How to easily access a web.config AppSettings value with a Type and a default value?
Update: This is now in my Rhyous.Collections NuGet package and you can see the source on GitHub: NameValueCollectionExtensions.cs
I wanted to make it easier to get a value from AppSettings in the web.config (or the app.config if you aren’t doing web) while converting to the proper type and having a default value.
Here is the syntax I started with that I didn’t like at all.
public static int MaxRetryAttempts = (string.IsNullOrWhiteSpace(ConfigurationManager.AppSettings["SmptRetries"])) ? int.Parse(ConfigurationManager.AppSettings["SmptRetries"]) : 3;
I decided I wanted to have the following syntax:
ConfigurationManager.AppSettings.Get<T>(string key, T defaultValue)
I found a blog post that got me started. His syntax was very close to what I wanted already. He didn’t have the default value and he wasn’t an extension method, but wow, was this a real help. I was unaware of TypeDescriptor.GetConverter and was going to basically roll my own with an massively ugly case statement. So I am very happy I found his post.
I created the following NameValueCollectionExtensions.cs file.
using System.Collections.Specialized; using System.ComponentModel; namespace Rhyous.Extensions { public static class NameValueCollectionExtensions { public static T Get<T>(this NameValueCollection collection, string key, T defaultValue) { var value = collection[key]; var converter = TypeDescriptor.GetConverter(typeof(T)); if (string.IsNullOrWhiteSpace(value) || !converter.IsValid(value)) { return defaultValue; } return (T)(converter.ConvertFromInvariantString(value)); } } }
Details
- ConfigurationManager.AppSettings is of Type System.Collections.Specialized.NameValueCollection. So my extension method must be for that type.
- I changed the author’s method to be an extension method using the “this” keyword.
- I changed from using ConfirationManager.AppSettings to use the first parameter, collection, defined by the “this” keyword.
- I added a parameter for a default value.
- I changed the method code to return the default value, instead of throwing an exception, if the setting in the web.config is missing or empty.
Usage
I have to retry sending emails. I want the SmtpRetries to be an int obtained from the web.config’s AppSettings and have a default value of 3.
public static int MaxRetryAttempts = ConfigurationManager.AppSettings.Get("SmptRetries", 3);
Unit Tests
I really only wrote unit tests for int conversion. I expect that is sufficient. But if you want to write tests for a double or a other type feel free.
using System; using System.Collections.Specialized; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Rhyous.Extensions.Tests { [TestClass] public class NameValueCollectionExtensionsTests { private const string Name = "Retries"; [TestMethod] public void IntValueExistsTest() { // Arrange const int defaultMaxRetries = 2; const int value = 3; var collection = new NameValueCollection { { Name, value.ToString() } }; // Act var actual = collection.Get(Name, defaultMaxRetries); // Assert Assert.AreEqual(value, actual, "Valid value should return the valid value."); } [TestMethod] public void IntValueDoesNotExistTest() { // Arrange const int defaultMaxRetries = 2; var collection = new NameValueCollection(); // Act var actual = collection.Get(Name, defaultMaxRetries); // Assert Assert.AreEqual(defaultMaxRetries, actual, "Missing value returns default."); } [TestMethod] public void IntValueIsEmptyStringTest() { // Arrange const int defaultMaxRetries = 2; var collection = new NameValueCollection { { Name, string.Empty } }; // Act var actual = collection.Get(Name, defaultMaxRetries); // Assert Assert.AreEqual(defaultMaxRetries, actual, "Empty string returns default."); } [TestMethod] public void IntValueIsWhiteSpaceStringTest() { // Arrange const int defaultMaxRetries = 2; var collection = new NameValueCollection { { Name, " " } }; // Act var actual = collection.Get(Name, defaultMaxRetries); // Assert Assert.AreEqual(defaultMaxRetries, actual, "Whitespace string returns default."); } [TestMethod] public void IntValueIsDoubleTest() { // Arrange const int defaultMaxRetries = 2; const double value = 3.5; var collection = new NameValueCollection { { Name, value.ToString() } }; // Act var actual = collection.Get(Name, defaultMaxRetries); // Assert Assert.AreEqual(defaultMaxRetries, actual, "Invalid value returns default."); } [TestMethod] public void IntValueIsCharsTest() { // Arrange const int defaultMaxRetries = 2; const string value = "abc"; var collection = new NameValueCollection { { Name, value } }; // Act var actual = collection.Get(Name, defaultMaxRetries); // Assert Assert.AreEqual(defaultMaxRetries, actual, "Invalid value returns default."); } } }
Thanks for creating NameValueCollectionExtensions. I found your link on StackOverflow but having only just joined SO I don't have sufficient "reputation" to UpVote it. However your code works a treat!
Ahh - so site is stripping out the < T > symbols
That is weird. I have a plugin that is supposed to prevent exactly that from happening. I wonder why it failed this time. Anyway, I fixed it.
Thanks!
Nice! - small typo in the code for the extension however:
should be:
Jim
Thanks. Fixed!
FYI, I have a link just below the comment box that says: How to post code in comments?